﻿// Copyright (c) 2010 2011 2012 2013 2014 唐锐(also tr0217)
// mailto:tr0217@163.com
// The earliest release time: 2012-11-14
// Last modification time:
// Accompanying files of necessity:
//        * NavigateMenuItem.cs
//        * NavigateMenuItemCollection.cs
//        * NavigateContextMenu.cs
//
// This file and the accompanying files of this project may be freely used provided the following
// conditions are met:
//        * This copyright statement is not removed or modified.
//        * The code is not sold in uncompiled form.  (Release as a compiled binary which is part
//          of an application is fine)
//        * The design, code, or compiled binaries are not "Re-branded".
//        
// Optional:
//        * Redistributions in binary form must reproduce the above copyright notice.
//        * I receive a fully licensed copy of the product (regardless of whether the product is
//          is free, shrinkwrap, or commercial).  This is optional, though if you release products
//          which use code I've contributed to, I would appreciate a fully licensed copy.
//
// In addition, you may not:
//        * Publicly release modified versions of the code or publicly release works derived from
//          the code without any kind of authorization.
//
// NO WARRANTY:
//        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//        ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
//        WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
//        IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
//        INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
//        NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
//        OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
//        WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
//        ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
//        OF SUCH DAMAGE.
//


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace TR0217.ControlEx
{
    [ToolboxBitmap(typeof(NavigateBar), "NavigateBar.bmp")]
    public class NavigateBar : Control
    {
        private int _radius = 7;
        public int Radius
        {
            get { return _radius; }
            set
            {
                _radius = value;
                this.Invalidate();
            }
        }
        private Color _beginColor = SystemColors.GradientActiveCaption;

        public System.Drawing.Color BeginColor
        {
            get { return _beginColor; }
            set 
            {
                _beginColor = value;
                this.Invalidate();
            }
        }

        private Color _endColor = SystemColors.ActiveCaption;

        public Color EndColor
        {
            get { return _endColor; }
            set 
            { 
                _endColor = value;
                this.Invalidate();
            }
        }

        private LinearGradientMode _gradientMode = LinearGradientMode.Vertical;

        public System.Drawing.Drawing2D.LinearGradientMode GradientMode
        {
            get { return _gradientMode; }
            set 
            { 
                _gradientMode = value;
                this.Invalidate();
            }
        }

        private Image _image = null;
        public System.Drawing.Image Image
        {
            get { return _image; }
            set 
            { 
                _image = value;
                this.Invalidate(_imageBounds);
            }
        }

        private Color _transparentKey = Color.Pink;
        public System.Drawing.Color TransparentKey
        {
            get { return _transparentKey; }
            set { _transparentKey = value; }
        }

        private Rectangle _imageBounds;
        public System.Drawing.Rectangle ImageBounds
        {
            get { return _imageBounds; }
        }

        private TriPadding _imagePadding;
        [DefaultValue(typeof(TriPadding), "0, 0,0,100")]
        public TriPadding ImagePadding
        {
            get { return _imagePadding; }
            set 
            {
                this.Invalidate(_imageBounds);
                _imagePadding = value;
                this.CalcImageBounds();
                this.Invalidate(_imageBounds);
            }
        }

        private void CalcImageBounds()
        {
            int h = Height - _imagePadding.Top - _imagePadding.Bottom;
            h = h < 0 ? 0 : h;
            _imageBounds = new Rectangle(_imagePadding.Side, _imagePadding.Top,
                _imagePadding.Width, h);
        }

        private Color _selectedForeColor = Color.MidnightBlue;
        public System.Drawing.Color SelectedForeColor
        {
            get { return _selectedForeColor; }
            set 
            {
                _selectedForeColor = value;
            }
        }

        private int _menuInterval = 5;
        public int MenuInterval
        {
            get { return _menuInterval; }
            set { _menuInterval = value; }
        }

        private Rectangle _menuBounds;
        private Padding _menuPaddding;
        [DefaultValue(typeof(TriPadding), "0, 0,0,0")]
        public Padding MenuPaddding
        {
            get { return _menuPaddding; }
            set 
            {
                this.Invalidate(_menuBounds);
                _menuPaddding = value;
                this.CalcMenuBounds();
                this.Invalidate(_menuBounds);
            }
        }

        private void CalcMenuBounds()
        {
            int w = Width-_menuPaddding.Left - _menuPaddding.Right;
            int h = Height - _menuPaddding.Top - _menuPaddding.Bottom;
            w = w < 0 ? 0 : w;
            h = h < 0 ? 0 : h;
            _menuBounds = new Rectangle(_menuPaddding.Left, _menuPaddding.Top,w,h);
        }

        

        private NavigateMenuItem _dropItem = new NavigateMenuItem(">>");
        ContextContainer _contextContainer;
        NavigateContextMenu _navigateContextMenu;

        //菜单和之间的空格的总长度
        private float _menuLongthF = 0;
        private List<NavigateMenuItem> _visibleMenus = new List<NavigateMenuItem>();

        private NavigateMenuItemCollection _items;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public NavigateMenuItemCollection Items
        {
            get { return _items; }
        }

        public NavigateBar()
        {
            SetStyle(
                ControlStyles.UserPaint |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.AllPaintingInWmPaint |
                //ControlStyles.SupportsTransparentBackColor |
                ControlStyles.ResizeRedraw, true);

            //this.SetStyle(ControlStyles.Selectable, false);
            //this.SetStyle(ControlStyles.StandardClick, true);
            //this.SetStyle(ControlStyles.StandardDoubleClick, true);
            //this.SetStyle(ControlStyles.DoubleBuffer, true);
            //this.SetStyle(ControlStyles.Opaque, true);
            this.UpdateStyles();
            _items = new NavigateMenuItemCollection(this);
            _imageBounds = new Rectangle(5, 5, 150, 45);
            _navigateContextMenu = new NavigateContextMenu(this);
            _navigateContextMenu.Size = new Size(50, 50);
            _contextContainer = new ContextContainer(_navigateContextMenu);
            _contextContainer.ContextMode = ContextMode.SingleSelect;
            _contextContainer.Opening += new CancelEventHandler(_contextContainer_Opening);
            _dropItem.SelectedChanged += new SelectedChangedHandler(_dropItem_SelectedChanged);
            _navigateContextMenu.ItemSelect += new SelectedChangedHandler(_navigateContextMenu_ItemSelect);
        }

        void _navigateContextMenu_ItemSelect(object sender, SelectedChangedEventArgs e)
        {
            _selectedItem = e.Item;
        }

        void _dropItem_SelectedChanged(object sender, SelectedChangedEventArgs e)
        {
            if (_dropItem.Selected)
            {
                _dropItem._selected = false;
                Point p = new Point((int)_dropItem.Left, (int)_dropItem.Top);
                p = this.PointToScreen(p);
                _contextContainer.Show(new Rectangle(p.X, p.Y,
                    (int)_dropItem.Width, (int)_dropItem.Height),this);
            }
        }

        void _contextContainer_Opening(object sender, CancelEventArgs e)
        {
            _navigateContextMenu.SelectedItem = _selectedItem;
            int width = 0;
            int height = 3;
            foreach (NavigateMenuItem item in _navigateContextMenu.Items)
            {
                item.Left = 3;
                item.Top = height;
                if (width < (int)item.Width)
                {
                    width = (int)item.Width;
                }
                height += (int)item.Height+3;
            }
            width += 16;

            _navigateContextMenu.Size = new Size(width, height);
        }

        #region Dispose

        /// <summary>Clean up any resources being used.</summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_image != null)
                {
                    _image.Dispose();
                }
                _navigateContextMenu.Dispose();
                _contextContainer.Dispose();
            }

            base.Dispose(disposing);
        }

        #endregion

        protected override void OnCreateControl()
        {
            this.CalcImageBounds();
            this.CalcMenuBounds();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (this.Height < 50)
            {
                this.Height = 50;
            }
            int minWidth = (_imagePadding.Side + ImagePadding.Width);
            if (this.Width < minWidth)
            {
                this.Width = minWidth;
            }
        }

        private bool _calcWidth = true;
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            this.CalcImageBounds();
            this.CalcMenuBounds();

            if (_radius != 0)
            {
                GraphicsPath shape = new GraphicsPath();
                shape.AddArc(-1, -1, _radius * 2, _radius * 2, 180, 90);
                shape.AddLine(_radius, 0, Width - _radius, 0);
                shape.AddArc(Width - _radius * 2, -1, _radius * 2, _radius * 2, 270, 90);
                shape.AddLines(new Point[] {
                    new Point(Width,_radius),
                    new Point(Width,Height),
                    new Point(0,Height),
                    new Point(0,_radius)});

                Region r = this.Region;
                this.Region = new Region(shape);
                shape.Dispose();
                if (r != null) r.Dispose();
            }
            else
            {
                Region r = this.Region;
                this.Region = null;
                if (r != null) r.Dispose();
            }
                
        }

        public void ForceCalcWidth()
        {
            _calcWidth = true;
            this.Invalidate();
        }

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            Rectangle rect = new Rectangle(0, 0, this.Width, this.Height);
            LinearGradientBrush gradientBrush = new LinearGradientBrush(rect, _beginColor, _endColor, _gradientMode);
            pevent.Graphics.FillRectangle(gradientBrush, rect);
            gradientBrush.Dispose();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (_image != null)
            {
                Bitmap bmp = new Bitmap(_image);
                bmp.MakeTransparent(_transparentKey);
                e.Graphics.DrawImage(bmp, _imageBounds);
                bmp.Dispose();
            }

            if (_calcWidth)
            {
                SizeF sf = e.Graphics.MeasureString(">>", this.Font);
                _dropItem.Width = sf.Width;
                _dropItem.Height = sf.Height;
                _menuLongthF = _menuInterval;
                foreach (NavigateMenuItem item in _items)
                {
                    if (item.Visible)
                    {
                        sf = e.Graphics.MeasureString(item.Text, this.Font);
                        item.Width = sf.Width;
                        item.Height = sf.Height;
                        _menuLongthF += (sf.Width + _menuInterval);
                    }
                }
                _calcWidth = false;
            }

            float width = _menuInterval;
            bool hasUnVisible = false;
            _visibleMenus.Clear();
            _navigateContextMenu.Items.Clear();

            foreach (NavigateMenuItem item in _items)
            {
                if (item != _selectedItem)
                    item.Selected = false;
                if (item.Visible)
                {
                    width += (item.Width + (float)_menuInterval);
                    if (width < _menuBounds.Width)
                        _visibleMenus.Add(item);
                    else
                    {
                        _navigateContextMenu.Items.Add(item);
                        hasUnVisible = true;
                    }
                }
            }

            if (hasUnVisible)
            {
                _visibleMenus.Add(_dropItem);
            }

            float x = (float)_menuBounds.Left + ((float)_menuBounds.Width - width) / 2;
            x = x < _menuBounds.Left ? _menuBounds.Left : x;
            float top = (float)_menuBounds.Top + (_menuBounds.Height - _dropItem.Height) / 2;

            foreach (NavigateMenuItem item in _visibleMenus)
            {
                item.Top = top;
                item.Left = x;
                x += (item.Width + _menuInterval);
            }

            SolidBrush brushFore = new SolidBrush(this.ForeColor);

            foreach (NavigateMenuItem item in _visibleMenus)
            {
                if (item.Selected)
                {
                    SolidBrush brushSelectedFore = new SolidBrush(this.SelectedForeColor);
                    if (item == _hotItem)
                    {
                        Font font = new Font(this.Font, FontStyle.Bold);
                        e.Graphics.DrawString(item.Text, font, brushSelectedFore, item.Left, item.Top);
                        font.Dispose();
                    }
                    else
                        e.Graphics.DrawString(item.Text, this.Font, brushSelectedFore, item.Left, item.Top);

                    brushSelectedFore.Dispose();
                } 
                else
                {
                    if (item == _hotItem)
                    {
                        Font font = new Font(this.Font, FontStyle.Bold);
                        e.Graphics.DrawString(item.Text, font, brushFore, item.Left, item.Top);
                        font.Dispose();
                    }
                    else
                        e.Graphics.DrawString(item.Text, this.Font, brushFore, item.Left, item.Top);
                }                
            }
            brushFore.Dispose();
        }

        NavigateMenuItem _hotItem = null;
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            _hotItem = null;
            foreach (NavigateMenuItem item in _visibleMenus)
            {
                Rectangle rect = new Rectangle((int)item.Left - _menuInterval / 2, (int)item.Top - _menuInterval / 2,
                    (int)item.Width + _menuInterval, (int)item.Height + _menuInterval);
                if (rect.Contains(e.Location))
                    _hotItem = item;
            }
            this.Invalidate();
        }

        NavigateMenuItem _selectedItem = null;
        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            if (_hotItem != null)
            {
                Rectangle rect = new Rectangle((int)_hotItem.Left - _menuInterval / 2, (int)_hotItem.Top - _menuInterval / 2,
                        (int)_hotItem.Width + _menuInterval, (int)_hotItem.Height + _menuInterval);
                if (rect.Contains(e.Location))
                {
                    _hotItem.Selected = true;
                    if (_hotItem != _dropItem)
                        _selectedItem = _hotItem;
                }
            }

            this.Invalidate();
        }
    }
   
}
